package com.sudo.aspect;
import com.sudo.common.enums.UserParams;
import com.sudo.common.utils.DateUtil;
import com.sudo.common.utils.IpAddressUtils;
import com.sudo.common.utils.JsonUtils;
import com.sudo.common.utils.RedisOperator;
import com.sudo.pojo.SysUserBehavior;
import com.sudo.service.BaseService;
import io.swagger.annotations.ApiOperation;
import org.apache.ibatis.javassist.*;
import org.apache.ibatis.javassist.bytecode.CodeAttribute;
import org.apache.ibatis.javassist.bytecode.LocalVariableAttribute;
import org.apache.ibatis.javassist.bytecode.MethodInfo;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * @author：为道日损 2020-04-24 11:05
 */
@Aspect
@Component
public class DbLogAspect extends BaseService {

    final static Logger logger = LoggerFactory.getLogger(DbLogAspect.class);
    private final static String[] uriArr = {"/sysInfo/getMemInfoActualTime","/sysInfo/getCpuInfoActualTime"};

    @Autowired
    private RedisOperator redisOperator;
    /**
     * 定义切入点 @Pintcut
     * 在注解的位置切入代码
     */
    @Pointcut("@annotation(io.swagger.annotations.ApiOperation)")
    public void logPointCut() {

    }

    /**
     * 切面 配置通知
     */
    @AfterReturning("logPointCut()")
    public void getDbLog(JoinPoint joinPoint) {

        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            int flag = Arrays.binarySearch(uriArr, request.getRequestURI());
            if (flag >= 0) {
                return;
            }

            SysUserBehavior sysUserBehavior = new SysUserBehavior();
            //从切面织入处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            //获取植入点所在的方法
            Method method = signature.getMethod();

            //获取操作
            ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
            if (null != apiOperation) {
                String value = apiOperation.value();
                sysUserBehavior.setOperEvent(value);
            }

            //获取请求的类名
            String className = joinPoint.getTarget().getClass().getName();
            //获取请求的方法名
            String methodName = method.getName();
            sysUserBehavior.setOperMethod(className + "." + methodName);

            //请求参数
            Object[] args = joinPoint.getArgs();
            Map<String, Object> nameAndArgs = null;
            nameAndArgs = getFieldsName(this.getClass(), className, methodName, args);

            if (nameAndArgs.containsKey("request")){
                nameAndArgs.replace("request", null);
            }
            if (nameAndArgs.containsKey("response")){
                nameAndArgs.replace("response", null);
            }
            if (nameAndArgs.containsKey("pwd")) {
                nameAndArgs.replace("pwd", null);
            }
            if (nameAndArgs.containsKey("result")) {
                nameAndArgs.replace("result",null);
            }
            //文件上传中夹带的参数
            if (nameAndArgs.containsKey("newFileName")) {
                nameAndArgs.replace("newFileName",null);
            }
            if (nameAndArgs.containsKey("timeStr")) {
                nameAndArgs.replace("timeStr",null);
            }
            if (nameAndArgs.containsKey("suffix")) {
                nameAndArgs.replace("suffix",null);
            }
            //将参数所在的数组转换为json
            sysUserBehavior.setReqParam(nameAndArgs.toString());
            sysUserBehavior.setOperTime(DateUtil.dateToStringWithTime());

            //获取IP

            String username = request.getHeader(UserParams.HEADER_USER_ID.value);
            sysUserBehavior.setUsername(username);
            String roleCode = getRoleCodeFromRedisByUsername(username);
            sysUserBehavior.setRoleCode(roleCode);
            sysUserBehavior.setClientIp(IpAddressUtils.getIpAdrress(request));
            sysUserBehavior.setReqUrl(request.getRequestURI());
            sysUserBehavior.setReqType(request.getMethod());
            sysUserBehavior.setLogType("");

            logger.info("插入用户行为记录日志");
            //sysUserBehaviorService.addSysUserBehavior(sysUserBehavior);
            redisOperator.lpush(UserParams.REDIS_USER_BEHAVIOR.value, JsonUtils.objectToJson(sysUserBehavior));
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    private Map<String,Object> getFieldsName(Class cls, String clazzName, String methodName, Object[] args) throws NotFoundException {
        Map<String,Object > map=new HashMap<String,Object>();

        ClassPool pool = ClassPool.getDefault();
        ClassClassPath classPath = new ClassClassPath(cls);
        pool.insertClassPath(classPath);

        CtClass cc = pool.get(clazzName);
        CtMethod cm = cc.getDeclaredMethod(methodName);
        MethodInfo methodInfo = cm.getMethodInfo();
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
        if (attr == null) {
            // exception
        }
        // String[] paramNames = new String[cm.getParameterTypes().length];
        int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
        for (int i = 0; i < cm.getParameterTypes().length; i++){
            //paramNames即参数名
            map.put( attr.variableName(i + pos),args[i]);
        }
        return map;
    }

}
